package com.spsoft.workflow.utils;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.spsoft.core.exception.BaseException;
import com.spsoft.workflow.common.CommonConstants;
import com.spsoft.workflow.common.WorkFlowConstants;
import com.spsoft.workflow.common.enums.ModeEnums;
import com.spsoft.workflow.model.dto.json.ChildNode;
import com.spsoft.workflow.model.dto.json.ConditionInfo;
import com.spsoft.workflow.model.dto.json.GroupsInfo;
import com.spsoft.workflow.model.dto.json.Properties;
import com.spsoft.workflow.service.IExpressionService;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.BpmnAutoLayout;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.delegate.ExecutionListener;
import org.flowable.engine.delegate.TaskListener;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.stream.Collectors;
import static com.spsoft.workflow.common.WorkFlowConstants.*;
import static org.flowable.bpmn.model.ImplementationType.IMPLEMENTATION_TYPE_CLASS;
import static org.flowable.bpmn.model.ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION;


public class BpmnModelUtils {

    private static String id(String prefix) {
        return prefix + "_" + UUID.randomUUID().toString().replace("-", "").toLowerCase();
    }


    public static BpmnModel assemBpmnModel(JSONObject jsonObject, ChildNode childNode, String remark,
                                           String formName, long templateId, Set<String> processVariables) {
        BpmnModel bpmnModel = new BpmnModel();
        List<SequenceFlow> sequenceFlows = Lists.newArrayList();
        Map<String, ChildNode> childNodeMap = new HashMap<>();
        bpmnModel.setTargetNamespace( "");//原来是机构id
        ExtensionAttribute extensionAttribute = new ExtensionAttribute();
        extensionAttribute.setName("DingDing");
        extensionAttribute.setNamespace("http://flowable.org/bpmn");
        extensionAttribute.setValue(jsonObject.toJSONString());
        Process process = new Process();
        process.setId(WorkFlowConstants.PROCESS_PREFIX + templateId);
        process.setName(formName);
        process.setDocumentation(remark);
        process.addAttribute(extensionAttribute);
        bpmnModel.addProcess(process);

        StartEvent startEvent = createStartEvent();
        process.addFlowElement(startEvent);
        String lastNode = null;
        try {
            lastNode = create(startEvent.getId(), childNode, process, bpmnModel, sequenceFlows, childNodeMap, processVariables);
        } catch (InvocationTargetException e) {
            e.printStackTrace();
            throw new BaseException("流程操作失败");
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new BaseException("流程操作失败");
        }
        EndEvent endEvent = createEndEvent();
        process.addFlowElement(endEvent);
        process.addFlowElement(connect(lastNode, endEvent.getId(), sequenceFlows, childNodeMap, process, processVariables));
//        List<FlowableListener> executionListeners = new ArrayList<>();
//        FlowableListener flowableListener = new FlowableListener();
//        flowableListener.setEvent(ExecutionListener.EVENTNAME_END);
//        flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
//        flowableListener.setImplementation("${processListener}");
//        executionListeners.add(flowableListener);
//        process.setExecutionListeners(executionListeners);
        byte[] bytes = new BpmnXMLConverter().convertToXML(bpmnModel);
        String xml = new String(bytes);
        new BpmnAutoLayout(bpmnModel).execute();
        return bpmnModel;
    }

    private static ServiceTask serviceTask(String name) {
        ServiceTask serviceTask = new ServiceTask();
        serviceTask.setName(name);
        return serviceTask;
    }

    public static SequenceFlow connect(String from, String to, List<SequenceFlow> sequenceFlows, Map<String, ChildNode> childNodeMap, Process process, Set<String> processVariables) {
        SequenceFlow flow = new SequenceFlow();
        String sequenceFlowId = id("sequenceFlow");
        if (process.getFlowElement(from) != null && process.getFlowElement(from) instanceof ExclusiveGateway) {
            ChildNode childNode = childNodeMap.get(to);
            if (childNode != null) {
                String parentId = childNode.getParentId();
                if (StringUtils.isNotBlank(parentId)) {
                    ChildNode parentNode = childNodeMap.get(parentId);
                    if (parentNode != null) {
                        if (Type.CONDITION.type.equals(parentNode.getType())) {
                            sequenceFlowId = parentNode.getId();
                            flow.setName(parentNode.getName());
                            //解析条件表达式
                            Properties props = parentNode.getProps();
                            String expression = props.getExpression();
                            List<GroupsInfo> groups = props.getGroups();
                            String groupsType = props.getGroupsType();
                            if (StringUtils.isNotBlank(expression)) {
                                flow.setConditionExpression("${" + expression + "}");
                            } else {

                                StringBuffer conditionExpression = new StringBuffer();
                                conditionExpression.append("${ ");

                                for (int i = 0; i < groups.size(); i++) {
                                    conditionExpression.append(" ( ");
                                    GroupsInfo group = groups.get(i);
                                    List<String> cids = group.getCids();
                                    processVariables.addAll(cids);
                                    String groupType = group.getGroupType();
                                    List<ConditionInfo> conditions = group.getConditions();
                                    for (int j = 0; j < conditions.size(); j++) {
                                        conditionExpression.append(" ");
                                        ConditionInfo condition = conditions.get(j);
                                        String compare = condition.getCompare();
                                        String id = condition.getId();
                                        String title = condition.getTitle();
                                        List<Object> value = condition.getValue();
                                        String valueType = condition.getValueType();
                                        if ("input".equals(valueType)) {
                                            if ("=".equals(compare)) {
                                                String str = StringUtils.join(value, ",");
                                                str = "'" + str + "'";
                                                conditionExpression.append(" " + EXPRESSION_CLASS + "strEqualsMethod(" + id + "," + str + ") ");
                                            } else {
                                                List<String> tempList = new ArrayList<>();
                                                for (Object o : value) {
                                                    String s = o.toString();
                                                    s = "'" + s + "'";
                                                    tempList.add(s);
                                                }
                                                String str = StringUtils.join(tempList, ",");
//                                                String str = StringUtils.join(value, ",");
                                                conditionExpression.append(" " + EXPRESSION_CLASS + "strContainsMethod(" + id + "," + str + ") ");
                                            }
                                        } else if ("number".equals(valueType)) {
                                            String str = StringUtils.join(value, ",");
                                            if ("=".equals(compare)) {
                                                conditionExpression.append(" " + id + " == " + str + " ");
                                            } else if (">".equals(compare)) {
                                                conditionExpression.append("" + id + " > " + str + " ");
                                            } else if (">=".equals(compare)) {
                                                conditionExpression.append(" " + id + " >= " + str + " ");
                                            } else if ("<".equals(compare)) {
                                                conditionExpression.append(" " + id + " < " + str + " ");
                                            } else if ("<=".equals(compare)) {
                                                conditionExpression.append(" " + id + " <= " + str + " ");
                                            } else if ("IN".equals(compare)) {
                                                conditionExpression.append(" " + EXPRESSION_CLASS + "numberContains(" + id + "," + str + ") ");
                                            } else if ("B".equals(compare)) {
                                                conditionExpression.append("  " + EXPRESSION_CLASS + "b(" + id + "," + str + ") ");
                                            } else if ("AB".equals(compare)) {
                                                conditionExpression.append("  " + EXPRESSION_CLASS + "ab(" + id + "," + str + ") ");
                                            } else if ("BA".equals(compare)) {
                                                conditionExpression.append("  " + EXPRESSION_CLASS + "ba(" + id + "," + str + ") ");
                                            } else if ("ABA".equals(compare)) {
                                                conditionExpression.append("  " + EXPRESSION_CLASS + "aba(" + id + "," + str + ") ");
                                            }
                                        } else if ("User".equals(valueType)) {
                                            List<String> userIds = new ArrayList<>();
                                            for (Object o : value) {
                                                JSONObject obj = (JSONObject) o;
                                                userIds.add(obj.getString("id"));
                                            }
                                            String str = StringUtils.join(userIds, ",");
                                            conditionExpression.append(" " + EXPRESSION_CLASS + "strContainsMethod(" + id + "," + str + ") ");
                                        } else if ("Dept".equals(valueType)) {
                                            List<String> userIds = new ArrayList<>();
                                            List<String> deptIds = new ArrayList<>();
                                            for (Object o : value) {
                                                JSONObject obj = (JSONObject) o;
                                                String type = obj.getString("type");
                                                if ("dept".equals(type)) {
                                                    deptIds.add(obj.getString("id"));
                                                } else {
                                                    userIds.add(obj.getString("id"));
                                                }
                                            }

                                        } else {
                                            continue;
                                        }

                                        if (conditions.size() > 1 && j != (conditions.size() - 1)) {
                                            if ("OR".equals(groupType)) {
                                                conditionExpression.append(" || ");
                                            } else {
                                                conditionExpression.append(" && ");
                                            }
                                        }

                                        if (i == (conditions.size() - 1)) {
                                            conditionExpression.append(" ");
                                        }
                                    }


                                    conditionExpression.append(" ) ");

                                    if (groups.size() > 1 && i != (groups.size() - 1)) {
                                        if ("OR".equals(groupsType)) {
                                            conditionExpression.append(" || ");
                                        } else {
                                            conditionExpression.append(" && ");
                                        }
                                    }


                                }
                                conditionExpression.append("} ");
                                flow.setConditionExpression(conditionExpression.toString());
                            }
                        }
                    }
                }
            }
        }
        flow.setId(sequenceFlowId);
        flow.setSourceRef(from);
        flow.setTargetRef(to);
        sequenceFlows.add(flow);
        return flow;
    }

    public static StartEvent createStartEvent() {
        StartEvent startEvent = new StartEvent();
        startEvent.setId(START_EVENT_ID);
        startEvent.setInitiator(CommonConstants.START_USER_ID);
        return startEvent;
    }

    public static EndEvent createEndEvent() {
        EndEvent endEvent = new EndEvent();
        endEvent.setId(END_EVENT_ID);
        return endEvent;
    }


    public static String create(String fromId, ChildNode flowNode, Process process, BpmnModel bpmnModel, List<SequenceFlow> sequenceFlows, Map<String, ChildNode> childNodeMap, Set<String> processVariables) throws InvocationTargetException, IllegalAccessException {
        String nodeType = flowNode.getType();
        if (Type.CONCURRENTS.isEqual(nodeType)) {
            return createParallelGatewayBuilder(fromId, flowNode, process, bpmnModel, sequenceFlows, childNodeMap, processVariables);
        } else if (Type.CONDITIONS.isEqual(nodeType)) {
            return createExclusiveGatewayBuilder(fromId, flowNode, process, bpmnModel, sequenceFlows, childNodeMap, processVariables);
        } else if (Type.USER_TASK.isEqual(nodeType) || Type.ROOT.isEqual(nodeType)) {
            childNodeMap.put(flowNode.getId(), flowNode);
            JSONObject incoming = flowNode.getIncoming();
            incoming.put("incoming", Collections.singletonList(fromId));
            String id = createTask(process, flowNode, sequenceFlows, childNodeMap, processVariables);
            // 如果当前任务还有后续任务，则遍历创建后续任务
            ChildNode children = flowNode.getChildren();
            if (Objects.nonNull(children) && StringUtils.isNotBlank(children.getId())) {
                return create(id, children, process, bpmnModel, sequenceFlows, childNodeMap, processVariables);
            } else {
                return id;
            }
        } else if (Type.DELAY.isEqual(nodeType)) {
            throw new BaseException("暂未实现");
        } else if (Type.TRIGGER.isEqual(nodeType)) {
            throw new BaseException("暂未实现");
        } else if (Type.CC.isEqual(nodeType)) {
            childNodeMap.put(flowNode.getId(), flowNode);
            JSONObject incoming = flowNode.getIncoming();
            incoming.put("incoming", Collections.singletonList(fromId));
            String id = createCCServiceTask(process, flowNode, sequenceFlows, childNodeMap, processVariables);
            // 如果当前任务还有后续任务，则遍历创建后续任务
            ChildNode children = flowNode.getChildren();
            if (Objects.nonNull(children) && StringUtils.isNotBlank(children.getId())) {
                return create(id, children, process, bpmnModel, sequenceFlows, childNodeMap, processVariables);
            } else {
                return id;
            }
        } else {
            throw new RuntimeException("未知节点类型: nodeType=" + nodeType);
        }
    }

    private static String createExclusiveGatewayBuilder(String formId, ChildNode flowNode, Process process, BpmnModel bpmnModel, List<SequenceFlow> sequenceFlows, Map<String, ChildNode> childNodeMap, Set<String> processVariables) throws InvocationTargetException, IllegalAccessException {
        childNodeMap.put(flowNode.getId(), flowNode);
        String name = flowNode.getName();
        String exclusiveGatewayId = flowNode.getId();
        ExclusiveGateway exclusiveGateway = new ExclusiveGateway();
        exclusiveGateway.setId(exclusiveGatewayId);
        exclusiveGateway.setName(name);
        process.addFlowElement(exclusiveGateway);
        process.addFlowElement(connect(formId, exclusiveGatewayId, sequenceFlows, childNodeMap, process, processVariables));

        if (Objects.isNull(flowNode.getBranchs()) && Objects.isNull(flowNode.getChildren())) {
            return exclusiveGatewayId;
        }
        List<ChildNode> flowNodes = flowNode.getBranchs();
        List<String> incoming = Lists.newArrayListWithCapacity(flowNodes.size());
        List<JSONObject> conditions = Lists.newCopyOnWriteArrayList();
        for (ChildNode element : flowNodes) {
            childNodeMap.put(element.getId(), element);
            ChildNode childNode = element.getChildren();

            String nodeName = element.getName();
            Properties props = element.getProps();
            String expression = props.getExpression();


            if (Objects.isNull(childNode) || StringUtils.isBlank(childNode.getId())) {

                incoming.add(exclusiveGatewayId);
                JSONObject condition = new JSONObject();
                condition.fluentPut("nodeName", nodeName)
                        .fluentPut("expression", expression)
                        .fluentPut("groups", props.getGroups())
                        .fluentPut("groupsType", props.getGroupsType());
                conditions.add(condition);
                continue;
            }
            // 只生成一个任务，同时设置当前任务的条件
            JSONObject incomingObj = childNode.getIncoming();
            incomingObj.put("incoming", Collections.singletonList(exclusiveGatewayId));
            String identifier = create(exclusiveGatewayId, childNode, process, bpmnModel, sequenceFlows, childNodeMap, processVariables);
            List<SequenceFlow> flows = sequenceFlows.stream().filter(flow -> StringUtils.equals(exclusiveGatewayId, flow.getSourceRef()))
                    .collect(Collectors.toList());
            flows.stream().forEach(
                    e -> {
                        if (StringUtils.isBlank(e.getName()) && StringUtils.isNotBlank(nodeName)) {
                            e.setName(nodeName);
                        }
                        // 设置条件表达式
                        if (Objects.isNull(e.getConditionExpression()) && StringUtils.isNotBlank(expression)) {
                            e.setConditionExpression(expression);
                        }
                    }
            );
            if (Objects.nonNull(identifier)) {
                incoming.add(identifier);
            }
        }


        ChildNode childNode = flowNode.getChildren();

        if (Objects.nonNull(childNode) && StringUtils.isNotBlank(childNode.getId())) {
            String parentId = childNode.getParentId();
            ChildNode parentChildNode = childNodeMap.get(parentId);
            boolean conFlag = Type.CONCURRENTS.type
                    .equals(parentChildNode.getType());
            if (!conFlag) {
                String type = childNode.getType();
                if (!Type.EMPTY.type.equals(type)) {
                } else {
                    if (Type.CONDITIONS.type.equals(parentChildNode.getType())) {
                        String endExId = parentChildNode.getId() + "end";
                        process.addFlowElement(createExclusiveGateWayEnd(endExId));
                        if (incoming == null || incoming.isEmpty()) {
                            return create(exclusiveGatewayId, childNode, process, bpmnModel, sequenceFlows,
                                    childNodeMap, processVariables);
                        } else {
                            JSONObject incomingObj = childNode.getIncoming();
                            // 所有 service task 连接 end exclusive gateway
                            incomingObj.put("incoming", incoming);
                            FlowElement flowElement = bpmnModel.getFlowElement(incoming.get(0));
                            // 1.0 先进行边连接, 暂存 nextNode
                            ChildNode nextNode = childNode.getChildren();
                            childNode.setChildren(null);
                            String identifier = endExId;
                            for (int i = 0; i < incoming.size(); i++) {
                                process.addFlowElement(connect(incoming.get(i), identifier, sequenceFlows, childNodeMap, process, processVariables));
                            }

                            //  针对 gateway 空任务分支 添加条件表达式
                            if (!conditions.isEmpty()) {
                                FlowElement flowElement1 = bpmnModel.getFlowElement(identifier);
                                // 获取从 gateway 到目标节点 未设置条件表达式的节点
                                List<SequenceFlow> flows = sequenceFlows.stream().filter(
                                                flow -> StringUtils.equals(flowElement1.getId(), flow.getTargetRef()))
                                        .filter(
                                                flow -> StringUtils.equals(flow.getSourceRef(), exclusiveGatewayId))
                                        .collect(Collectors.toList());
                                flows.stream().forEach(sequenceFlow -> {
                                    if (!conditions.isEmpty()) {
                                        JSONObject condition = conditions.get(0);
                                        String nodeName = condition.getString("nodeName");
                                        String expression = condition.getString("expression");

                                        if (StringUtils.isBlank(sequenceFlow.getName()) && StringUtils
                                                .isNotBlank(nodeName)) {
                                            sequenceFlow.setName(nodeName);
                                        }
                                        // 设置条件表达式
                                        if (Objects.isNull(sequenceFlow.getConditionExpression())
                                                && StringUtils.isNotBlank(expression)) {
                                            sequenceFlow.setConditionExpression(expression);
                                        }

                                        conditions.remove(0);
                                    }
                                });

                            }

                            // 1.1 边连接完成后，在进行 nextNode 创建
                            if (Objects.nonNull(nextNode) && StringUtils.isNotBlank(nextNode.getId())) {
                                return create(identifier, nextNode, process, bpmnModel, sequenceFlows,
                                        childNodeMap, processVariables);
                            } else {
                                return identifier;
                            }
                        }


                    }
                }
            } else {

            }
        }
        return exclusiveGatewayId;
    }

    private static ExclusiveGateway createExclusiveGateWayEnd(String id) {
        ExclusiveGateway exclusiveGateway = new ExclusiveGateway();
        exclusiveGateway.setId(id);
        return exclusiveGateway;
    }

    private static ParallelGateway createParallelGateWayEnd(String id) {
        ParallelGateway parallelGateway = new ParallelGateway();
        parallelGateway.setId(id);
        return parallelGateway;
    }

    private static String createParallelGatewayBuilder(String formId, ChildNode flowNode, Process process, BpmnModel bpmnModel, List<SequenceFlow> sequenceFlows, Map<String, ChildNode> childNodeMap, Set<String> processVariables) throws InvocationTargetException, IllegalAccessException {
        childNodeMap.put(flowNode.getId(), flowNode);
        String name = flowNode.getName();
        ParallelGateway parallelGateway = new ParallelGateway();
        String parallelGatewayId = flowNode.getId();
        parallelGateway.setId(parallelGatewayId);
        parallelGateway.setName(name);
        process.addFlowElement(parallelGateway);
        process.addFlowElement(connect(formId, parallelGatewayId, sequenceFlows, childNodeMap, process, processVariables));

        if (Objects.isNull(flowNode.getBranchs()) && Objects.isNull(flowNode.getChildren())) {
            return parallelGatewayId;
        }

        List<ChildNode> flowNodes = flowNode.getBranchs();
        List<String> incoming = Lists.newArrayListWithCapacity(flowNodes.size());
        for (ChildNode element : flowNodes) {
            childNodeMap.put(element.getId(), element);
            ChildNode childNode = element.getChildren();
            if (Objects.isNull(childNode) || StringUtils.isBlank(childNode.getId())) {
                incoming.add(parallelGatewayId);
                continue;
            }
            String identifier = create(parallelGatewayId, childNode, process, bpmnModel, sequenceFlows, childNodeMap, processVariables);
            if (Objects.nonNull(identifier)) {
                incoming.add(identifier);
            }
        }

        ChildNode childNode = flowNode.getChildren();
        if (Objects.nonNull(childNode) && StringUtils.isNotBlank(childNode.getId())) {
            String parentId = childNode.getParentId();
            ChildNode parentChildNode = childNodeMap.get(parentId);
            boolean conFlag = Type.CONCURRENTS.type
                    .equals(parentChildNode.getType());
            if (!conFlag) {
                String type = childNode.getType();
                if (!Type.EMPTY.type.equals(type)) {

                } else {
                    if (Type.CONCURRENTS.type.equals(parentChildNode.getType())) {
                        String endExId = parentChildNode.getId() + "end";
                        process.addFlowElement(createParallelGateWayEnd(endExId));
                        // 普通结束网关
                        if (CollectionUtils.isEmpty(incoming)) {
                            return create(parallelGatewayId, childNode, process, bpmnModel, sequenceFlows, childNodeMap, processVariables);
                        } else {
                            JSONObject incomingObj = childNode.getIncoming();
                            // 所有 service task 连接 end parallel gateway
                            incomingObj.put("incoming", incoming);
                            FlowElement flowElement = bpmnModel.getFlowElement(incoming.get(0));
                            // 1.0 先进行边连接, 暂存 nextNode
                            ChildNode nextNode = childNode.getChildren();
                            childNode.setChildren(null);
                            String identifier = endExId;
                            for (int i = 0; i < incoming.size(); i++) {
                                FlowElement flowElement1 = bpmnModel.getFlowElement(incoming.get(i));
                                process.addFlowElement(connect(flowElement1.getId(), identifier, sequenceFlows, childNodeMap, process, processVariables));
                            }
                            // 1.1 边连接完成后，在进行 nextNode 创建
                            if (Objects.nonNull(nextNode) && StringUtils.isNotBlank(nextNode.getId())) {
                                return create(identifier, nextNode, process, bpmnModel, sequenceFlows, childNodeMap, processVariables);
                            } else {
                                return identifier;
                            }
                        }
                    }
                }
            } else {
                String type = childNode.getType();
                if (!Type.EMPTY.type.equals(type)) {

                } else {
                    if (Type.CONCURRENTS.type.equals(parentChildNode.getType())) {
                        String endExId = parentChildNode.getId() + "end";
                        process.addFlowElement(createParallelGateWayEnd(endExId));
                        // 普通结束网关
                        if (CollectionUtils.isEmpty(incoming)) {
                            return create(parallelGatewayId, childNode, process, bpmnModel, sequenceFlows, childNodeMap, processVariables);
                        } else {
                            JSONObject incomingObj = childNode.getIncoming();
                            // 所有 service task 连接 end parallel gateway
                            incomingObj.put("incoming", incoming);
                            FlowElement flowElement = bpmnModel.getFlowElement(incoming.get(0));
                            // 1.0 先进行边连接, 暂存 nextNode
                            ChildNode nextNode = childNode.getChildren();
                            childNode.setChildren(null);
                            String identifier = endExId;
                            for (int i = 0; i < incoming.size(); i++) {
                                FlowElement flowElement1 = bpmnModel.getFlowElement(incoming.get(i));
                                process.addFlowElement(connect(flowElement1.getId(), identifier, sequenceFlows, childNodeMap, process, processVariables));
                            }
                            // 1.1 边连接完成后，在进行 nextNode 创建
                            if (Objects.nonNull(nextNode) && StringUtils.isNotBlank(nextNode.getId())) {
                                return create(identifier, nextNode, process, bpmnModel, sequenceFlows, childNodeMap, processVariables);
                            } else {
                                return identifier;
                            }
                        }
                    }
                }
            }

        }
        return parallelGatewayId;
    }

    private static String createTask(Process process, ChildNode flowNode, List<SequenceFlow> sequenceFlows, Map<String, ChildNode> childNodeMap, Set<String> processVariables) {
        JSONObject incomingJson = flowNode.getIncoming();
        List<String> incoming = incomingJson.getJSONArray("incoming").toJavaList(String.class);
        // 自动生成id
//        String id = id("serviceTask");
        String id = flowNode.getId();
        if (incoming != null && !incoming.isEmpty()) {
            UserTask userTask = new UserTask();
            userTask.setName(flowNode.getName());
            userTask.setId(id);
            //审批超时配置
            JSONObject timeLimit = flowNode.getProps().getTimeLimit();
            if (timeLimit != null && !timeLimit.isEmpty()) {
                JSONObject timeout = timeLimit.getJSONObject("timeout");
                if (timeout != null && !timeout.isEmpty()) {
                    String unit = timeout.getString("unit");
                    Integer value = Convert.toInt(timeout.getInteger("value"));
                    if (value > 0) {
                        List<BoundaryEvent> boundaryEvents = new ArrayList<>();
                        BoundaryEvent boundaryEvent = new BoundaryEvent();
                        boundaryEvent.setId("BoundaryEvent_" + id);
                        boundaryEvent.setAttachedToRefId(id);
                        boundaryEvent.setAttachedToRef(userTask);
                        boundaryEvent.setCancelActivity(false);
                        TimerEventDefinition timerEventDefinition = new TimerEventDefinition();
                        timerEventDefinition.setTimeDuration("PT" + value + unit);
                        timerEventDefinition.setId("TimerEventDefinition_" + id);
                        boundaryEvent.addEventDefinition(timerEventDefinition);
                        FlowableListener activitiListener = new FlowableListener();
                        activitiListener.setEvent(ExecutionListener.EVENTNAME_END);
                        activitiListener.setImplementationType(IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
                        activitiListener.setImplementation("${timeEventListener}");

                        List<FlowableListener> listeners = new ArrayList<>();
                        listeners.add(activitiListener);
                        boundaryEvent.setExecutionListeners(listeners);
                        process.addFlowElement(boundaryEvent);
                        boundaryEvents.add(boundaryEvent);
                        userTask.setBoundaryEvents(boundaryEvents);
                    }
                }
            }


            process.addFlowElement(userTask);
            process.addFlowElement(connect(incoming.get(0), id, sequenceFlows, childNodeMap, process, processVariables));

            ArrayList<FlowableListener> taskListeners = new ArrayList<>();
            FlowableListener taskListener = new FlowableListener();
            // 事件类型,
            taskListener.setEvent(TaskListener.EVENTNAME_CREATE);
            // 监听器类型
            taskListener.setImplementationType(IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
            // 设置实现了，这里设置监听器的类型是delegateExpression，这样可以在实现类注入Spring bean.
            taskListener.setImplementation("${taskCreatedListener}");
            taskListeners.add(taskListener);
            userTask.setTaskListeners(taskListeners);
            if ("root".equalsIgnoreCase(id)) {
            } else {
                ArrayList<FlowableListener> listeners = new ArrayList<>();
                FlowableListener activitiListener = new FlowableListener();
                // 事件类型,
                activitiListener.setEvent(ExecutionListener.EVENTNAME_START);
                // 监听器类型
                activitiListener.setImplementationType(IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
                // 设置实现了，这里设置监听器的类型是delegateExpression，这样可以在实现类注入Spring bean.
                activitiListener.setImplementation("${counterSignListener}");
                listeners.add(activitiListener);
                userTask.setExecutionListeners(listeners);
                Properties props = flowNode.getProps();
                String mode = props.getMode();
                MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = new MultiInstanceLoopCharacteristics();
                // 审批人集合参数
                multiInstanceLoopCharacteristics.setInputDataItem(userTask.getId() + "assigneeList");
                // 迭代集合
                multiInstanceLoopCharacteristics.setElementVariable("assigneeName");
                // 并行
                multiInstanceLoopCharacteristics.setSequential(false);
                userTask.setAssignee("${assigneeName}");
                // 设置多实例属性
                userTask.setLoopCharacteristics(multiInstanceLoopCharacteristics);
                if (ModeEnums.OR.getTypeName().equals(mode)) {
                    multiInstanceLoopCharacteristics.setCompletionCondition("${nrOfCompletedInstances/nrOfInstances > 0}");
                } else if (ModeEnums.NEXT.getTypeName().equals(mode)) {
                    multiInstanceLoopCharacteristics.setSequential(true);
                }
            }
        }
        return id;
    }

    /**
     * 创建抄送任务
     *
     * @param process
     * @param flowNode
     * @param sequenceFlows
     * @param childNodeMap
     * @param processVariables
     * @return
     */
    private static String createCCServiceTask(Process process, ChildNode flowNode, List<SequenceFlow> sequenceFlows, Map<String, ChildNode> childNodeMap, Set<String> processVariables) {
        JSONObject incomingJson = flowNode.getIncoming();
        List<String> incoming = incomingJson.getJSONArray("incoming").toJavaList(String.class);
        String id = flowNode.getId();
        if (incoming != null && !incoming.isEmpty()) {
            ServiceTask serviceTask = new ServiceTask();
            serviceTask.setName(flowNode.getName());
            serviceTask.setId(id);
            process.addFlowElement(serviceTask);
            process.addFlowElement(connect(incoming.get(0), id, sequenceFlows, childNodeMap, process, processVariables));
            serviceTask.setImplementationType(IMPLEMENTATION_TYPE_CLASS);
            serviceTask.setImplementation("com.hwsoft.workflow.common.listener.CCServiceDelegate");
        }
        return id;
    }

    /**
     * 流程节点类型
     */
    @Getter
    public enum Type {

        /**
         * 并行事件
         */
        CONCURRENTS("CONCURRENTS", ParallelGateway.class),
        CONCURRENT("CONCURRENT", SequenceFlow.class),
        /**
         * 排他事件
         */
        CONDITION("CONDITION", ExclusiveGateway.class),
        CONDITIONS("CONDITIONS", ExclusiveGateway.class),
        /**
         * 任务
         */
        USER_TASK("APPROVAL", UserTask.class),
        EMPTY("EMPTY", Object.class),
        ROOT("ROOT", UserTask.class),
        CC("CC", ServiceTask.class),
        TRIGGER("TRIGGER", ServiceTask.class),
        DELAY("DELAY", IntermediateCatchEvent.class);
        private String type;

        private Class<?> typeClass;

        Type(String type, Class<?> typeClass) {
            this.type = type;
            this.typeClass = typeClass;
        }

        public final static Map<String, Class<?>> TYPE_MAP = Maps.newHashMap();

        static {
            for (Type element : Type.values()) {
                TYPE_MAP.put(element.type, element.typeClass);
            }
        }

        public boolean isEqual(String type) {
            return this.type.equals(type);
        }

    }


    public static ChildNode getChildNode(ChildNode childNode, String nodeId) {
        Map<String, ChildNode> childNodeMap = new HashMap<>();
        if (StringUtils.isNotBlank(childNode.getId())) {
            getChildNode(childNode, childNodeMap);
        }

        Set<String> set = childNodeMap.keySet();
        for (String s : set) {
            if (StringUtils.isNotBlank(s)) {
                if (s.equals(nodeId)) {
                    return childNodeMap.get(s);
                }
            }
        }
        return null;
    }

    public static void getChildNode(ChildNode childNode, Map<String, ChildNode> childNodeMap) {
        childNodeMap.put(childNode.getId(), childNode);
        List<ChildNode> branchs = childNode.getBranchs();
        ChildNode children = childNode.getChildren();
        if (branchs != null && branchs.size() > 0) {
            for (ChildNode branch : branchs) {
                if (StringUtils.isNotBlank(branch.getId())) {
                    childNodeMap.put(branch.getId(), branch);
                    getChildNode(branch, childNodeMap);
                }
            }
        }

        if (children != null) {
            childNodeMap.put(children.getId(), children);
            getChildNode(children, childNodeMap);
        }

    }

    /**
     * 获取全部用户审批(抄送)任务
     *
     * @param userTasknodeLIst 用户审批任务
     * @param ccTasknodeLIst   用户抄送任务
     * @param bpmnModel        bpmn模型
     * @param processVariables 流程判断变量
     * @param nodeId           需要获取哪个节点之后的用户任务
     */
    public static void getNextUserTaskNode(List<ChildNode> userTasknodeLIst, List<ChildNode> ccTasknodeLIst, BpmnModel bpmnModel, Map<String, Object> processVariables, String nodeId) {
        String dingDing = bpmnModel.getMainProcess().getAttributeValue("http://flowable.org/bpmn", "DingDing");
        JSONObject jsonObject = JSONObject.parseObject(dingDing, new TypeReference<JSONObject>() {
        });
        String processJson = jsonObject.getString("processJson");
        ChildNode childNode = JSONObject.parseObject(processJson, new TypeReference<ChildNode>() {
        });
        ChildNode children = BpmnModelUtils.getChildNode(childNode, nodeId).getChildren();
        String type = children.getType();
        if (Type.CONDITIONS.getType().equals(type)) {
            List<ChildNode> branchs = children.getBranchs();
            for (ChildNode branch : branchs) {
                SequenceFlow flowElement = (SequenceFlow) bpmnModel.getMainProcess().getFlowElement(branch.getId());
                String conditionExpression = flowElement.getConditionExpression();
                FlowJuels flowJuels = SpringUtil.getBean(FlowJuels.class);
                Boolean expBooleanValue = SpringUtil.getBean(IExpressionService.class).getBooleanValue(processVariables, conditionExpression, false);
                if (expBooleanValue) {
                    ChildNode children1 = branch.getChildren();
                    if (Type.USER_TASK.getType().equals(children1.getType())) {
                        userTasknodeLIst.add(children1);
                        getNextUserTaskNode(userTasknodeLIst, ccTasknodeLIst, bpmnModel, processVariables, children1.getId());
                    }
                }
            }
            ChildNode children2 = children.getChildren();
            getNextUserTaskNode(userTasknodeLIst, ccTasknodeLIst, bpmnModel, processVariables, children2.getId());
        } else if (Type.USER_TASK.getType().equals(type)) {
            userTasknodeLIst.add(children);
            getNextUserTaskNode(userTasknodeLIst, ccTasknodeLIst, bpmnModel, processVariables, children.getId());
        } else if (Type.CC.getType().equals(type)) {
            ccTasknodeLIst.add(children);
            getNextUserTaskNode(userTasknodeLIst, ccTasknodeLIst, bpmnModel, processVariables, children.getId());
        } else if (Type.CONCURRENTS.getType().equals(type)) {
            List<ChildNode> branchs = children.getBranchs();
            for (ChildNode branch : branchs) {
                ChildNode children1 = branch.getChildren();
                if (Type.USER_TASK.getType().equals(children1.getType())) {
                    userTasknodeLIst.add(children1);
                    getNextUserTaskNode(userTasknodeLIst, ccTasknodeLIst, bpmnModel, processVariables, children1.getId());
                } else if (Type.CC.getType().equals(children1.getType())) {
                    ccTasknodeLIst.add(children1);
                    getNextUserTaskNode(userTasknodeLIst, ccTasknodeLIst, bpmnModel, processVariables, children1.getId());
                }
            }
            ChildNode children2 = children.getChildren();
            getNextUserTaskNode(userTasknodeLIst, ccTasknodeLIst, bpmnModel, processVariables, children2.getId());
        }
    }


    /**
     * 获取流程定义并行网关内所有节点
     *
     * @param procDefId 流程定义
     * @return 并行网关内所有节点
     */
    public static Map<String, ChildNode> getParallelMap(String procDefId) {
        ChildNode childNode = getFlowJson(procDefId);
        Map<String, ChildNode> childNodeMap = new HashMap<>();
        Map<String, ChildNode> parallelUserTaskMap = new HashMap<>();
        BpmnModelUtils.getChildNode(childNode, childNodeMap);
        childNodeMap.forEach((key, value) -> {
            if (value != null && StrUtil.isNotBlank(key) && StrUtil.isNotBlank(value.getType()) && value.getType().equals(Type.CONCURRENTS.getType())) {
                List<ChildNode> branchs = value.getBranchs();
                for (ChildNode branch : branchs) {
                    BpmnModelUtils.getChildNode(branch, parallelUserTaskMap);
                }
            }
        });
        return parallelUserTaskMap;
    }

    /**
     * 获取流程定义并行网关内所有节点
     *
     * @param bpmnModel 流程模型
     * @return 并行网关内所有节点
     */
    public static Map<String, ChildNode> getParallelMap(BpmnModel bpmnModel) {
        Process mainProcess = bpmnModel.getMainProcess();
        String dingDing = mainProcess.getAttributeValue(FLOWABLE_NAMESPACE, DING_DING);
        JSONObject jsonObject = JSON.parseObject(dingDing, new TypeReference<JSONObject>() {
        });
        String processJson = jsonObject.getString("processJson");
        ChildNode childNode = JSON.parseObject(processJson, new TypeReference<ChildNode>() {
        });
        Map<String, ChildNode> childNodeMap = new HashMap<>();
        Map<String, ChildNode> parallelUserTaskMap = new HashMap<>();
        BpmnModelUtils.getChildNode(childNode, childNodeMap);
        childNodeMap.forEach((key, value) -> {
            if (value != null && StrUtil.isNotBlank(key) && StrUtil.isNotBlank(value.getType()) && value.getType().equals(Type.CONCURRENTS.getType())) {
                List<ChildNode> branchs = value.getBranchs();
                for (ChildNode branch : branchs) {
                    BpmnModelUtils.getChildNode(branch, parallelUserTaskMap);
                }
            }
        });
        return parallelUserTaskMap;
    }

    /**
     * 获取并行网关内下属第一级用户任务
     *
     * @param procDefId 流程定义
     * @return 并行网关内下属第一级用户任务
     */
    public static Map<String, ChildNode> getParalleGateWayFirstUserTasklMap(String procDefId) {
        Map<String, ChildNode> parallelMap = getParallelMap(procDefId);
        Map<String, ChildNode> parallelFirstUserTaskMap = new HashMap<>();
        parallelMap.forEach((key, value) -> {
            if (value != null && StrUtil.isNotBlank(key) && StrUtil.isNotBlank(value.getType()) && value.getType().equals(Type.USER_TASK.getType())) {
                String parentId = value.getParentId();
                //是否是并行网关内下属第一级用户任务
                boolean flag = true;
                for (int i = 0; i < 50; i++) {
                    if (StrUtil.isBlank(parentId) || "null".equals(parentId)) {
                        break;
                    }
                    ChildNode childNode1 = parallelMap.get(parentId);
                    if (childNode1 == null) {
                        break;
                    }
                    if (childNode1.getType().equals(Type.USER_TASK.getType())) {
                        flag = false;
                        parentId = childNode1.getParentId();
                    }
                }
                if (flag) {
                    parallelFirstUserTaskMap.put(key, value);
                }
            }
        });
        return parallelFirstUserTaskMap;
    }

    /**
     * 根据流程定义id获取流程json
     *
     * @param procDefId
     * @return
     */
    public static ChildNode getFlowJson(String procDefId) {
        Process mainProcess = getMainProcess(procDefId);
        String dingDing = mainProcess.getAttributeValue(FLOWABLE_NAMESPACE, DING_DING);
        JSONObject jsonObject = JSON.parseObject(dingDing, new TypeReference<JSONObject>() {
        });
        String processJson = jsonObject.getString("processJson");
        return JSON.parseObject(processJson, new TypeReference<ChildNode>() {
        });
    }

    public static Process getMainProcess(String procDefId) {
        RepositoryService repositoryService = SpringUtil.getBean(RepositoryService.class);
        return repositoryService.getBpmnModel(procDefId).getMainProcess();
    }
}
